package org.microframework.base.core.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.CaseInsensitiveContainerFactory;
import com.jfinal.plugin.activerecord.IDataSourceProvider;
import com.jfinal.plugin.activerecord.cache.EhCache;
import com.jfinal.plugin.activerecord.dialect.MysqlDialect;
import com.jfinal.plugin.activerecord.dialect.PostgreSqlDialect;
import com.jfinal.plugin.activerecord.dialect.OracleDialect;
import com.jfinal.plugin.activerecord.dialect.SqlServerDialect;
import com.jfinal.plugin.activerecord.dialect.Sqlite3Dialect;
import com.jfinal.plugin.activerecord.dialect.H2Dialect;
import com.jfinal.json.JFinalJson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.sql.Connection;

/**
 * JFinal ActiveRecord 配置类
 * 负责初始化 JFinal ActiveRecord 插件
 */
@Slf4j
@Configuration
@ConditionalOnProperty(prefix = "micro.datasource.active-record", name = "enabled", havingValue = "true", matchIfMissing = true)
public class ActiveRecordConfiguration implements InitializingBean, DisposableBean {

	@Autowired
	private DataSource dataSource;
	
	@Autowired
	private DatabaseProperties databaseProperties;
	
	private ActiveRecordPlugin activeRecordPlugin;

	@Override
	public void afterPropertiesSet() throws Exception {
		log.info("初始化 JFinal ActiveRecord 插件");
		
		// 创建 ActiveRecordPlugin 实例
		this.activeRecordPlugin = createActiveRecordPlugin();
		
		// 设置方言
		configureDialect();
		
		// 高级配置
		configureAdvancedSettings();
		
		// 配置缓存
		configureCache();
		
		// 加载映射类
		loadMappingClasses();
		
		// 配置JSON转换
		configureJson();
		
		// 启动插件
		this.activeRecordPlugin.start();
		
		log.info("JFinal ActiveRecord 插件初始化完成");
	}

	@Override
	public void destroy() throws Exception {
		if (this.activeRecordPlugin != null) {
			log.info("停止 JFinal ActiveRecord 插件");
			this.activeRecordPlugin.stop();
		}
	}
	
	/**
	 * 创建 ActiveRecordPlugin 实例
	 */
	private ActiveRecordPlugin createActiveRecordPlugin() {
		String datasourceName = databaseProperties.getActiveRecord().getDatasourceName();
		if (StringUtils.hasText(datasourceName)) {
			log.info("使用命名数据源: {}", datasourceName);
		} else {
			datasourceName = "main";
			log.info("使用默认数据源名: {}", datasourceName);
		}
		
		ActiveRecordPlugin arp = new ActiveRecordPlugin(datasourceName, new IDataSourceProvider() {
			@Override
			public DataSource getDataSource() {
				return dataSource;
			}
		});
		
		// 设置开发模式
		boolean devMode = databaseProperties.getActiveRecord().isDevMode();
		arp.setDevMode(devMode);
		log.info("ActiveRecord 开发模式: {}", devMode);
		
		// 设置是否显示SQL
		boolean showSql = databaseProperties.getActiveRecord().isShowSql();
		arp.setShowSql(showSql);
		log.info("ActiveRecord 显示SQL: {}", showSql);
		
		// 设置事务级别
		int transactionLevel = databaseProperties.getActiveRecord().getTransactionLevel();
		if (transactionLevel == 0) {
			transactionLevel = Connection.TRANSACTION_READ_COMMITTED;
		}
		arp.setTransactionLevel(transactionLevel);
		log.info("ActiveRecord 事务级别: {}", transactionLevel);
		
		// 设置SQL模板
		arp.getEngine().setToClassPathSourceFactory();
		
		// 设置不区分大小写容器
		boolean caseInsensitive = databaseProperties.getActiveRecord().isCaseInsensitive();
		if (caseInsensitive) {
			arp.setContainerFactory(new CaseInsensitiveContainerFactory());
			log.info("ActiveRecord 启用不区分大小写容器");
		}
		
		return arp;
	}
	
	/**
	 * 设置数据库方言
	 */
	private void configureDialect() {
		String dbType = getDatabaseType();
		log.info("数据库类型: {}", dbType);
		
		// 根据数据库类型设置方言
		if ("mysql".equalsIgnoreCase(dbType)) {
			this.activeRecordPlugin.setDialect(new MysqlDialect());
		} else if ("postgresql".equalsIgnoreCase(dbType)) {
			this.activeRecordPlugin.setDialect(new PostgreSqlDialect());
		} else if ("oracle".equalsIgnoreCase(dbType)) {
			this.activeRecordPlugin.setDialect(new OracleDialect());
		} else if ("sqlserver".equalsIgnoreCase(dbType)) {
			this.activeRecordPlugin.setDialect(new SqlServerDialect());
		} else if ("sqlite".equalsIgnoreCase(dbType)) {
			this.activeRecordPlugin.setDialect(new Sqlite3Dialect());
		} else if ("h2".equalsIgnoreCase(dbType)) {
			this.activeRecordPlugin.setDialect(new H2Dialect());
		} else {
			log.info("未识别的数据库类型 [{}]，使用MySQL方言", dbType);
			this.activeRecordPlugin.setDialect(new MysqlDialect());
		}
	}
	
	/**
	 * 配置高级设置
	 */
	private void configureAdvancedSettings() {
		// 设置批处理大小
		int batchSize = databaseProperties.getActiveRecord().getBatchSize();
		if (batchSize > 0) {
			this.activeRecordPlugin.setBaseSqlTemplatePath("sql");
			log.info("ActiveRecord 批处理大小: {}", batchSize);
		}
		
		// 设置超时时间
		int queryTimeOut = databaseProperties.getActiveRecord().getQueryTimeOut();
		if (queryTimeOut > 0) {
			this.activeRecordPlugin.setTransactionLevel(queryTimeOut);
			log.info("ActiveRecord 查询超时: {}", queryTimeOut);
		}
		
		// 设置SQL格式化
		this.activeRecordPlugin.getEngine().setCompressorOn(' ');
	}
	
	/**
	 * 配置缓存
	 */
	private void configureCache() {
		if (databaseProperties.getActiveRecord().isEnableCache()) {
			// 默认使用EhCache
			this.activeRecordPlugin.setCache(new EhCache());
			log.info("ActiveRecord 启用缓存");
		}
	}
	
	/**
	 * 获取数据库类型
	 */
	private String getDatabaseType() {
		// 尝试从 DatabaseProperties 获取数据库类型
		String type = databaseProperties.getType();
		if (StringUtils.hasText(type)) {
			if (type.toLowerCase().contains("mysql")) {
				return "mysql";
			} else if (type.toLowerCase().contains("postgresql")) {
				return "postgresql";
			} else if (type.toLowerCase().contains("oracle")) {
				return "oracle";
			} else if (type.toLowerCase().contains("sqlserver")) {
				return "sqlserver";
			} else if (type.toLowerCase().contains("sqlite")) {
				return "sqlite";
			} else if (type.toLowerCase().contains("h2")) {
				return "h2";
			}
		}
		
		// 尝试从数据源获取数据库类型
		if (dataSource instanceof DruidDataSource) {
			String url = ((DruidDataSource) dataSource).getUrl();
			if (url != null) {
				if (url.startsWith("jdbc:mysql:")) {
					return "mysql";
				} else if (url.startsWith("jdbc:postgresql:")) {
					return "postgresql";
				} else if (url.startsWith("jdbc:oracle:")) {
					return "oracle";
				} else if (url.startsWith("jdbc:sqlserver:")) {
					return "sqlserver";
				} else if (url.startsWith("jdbc:sqlite:")) {
					return "sqlite";
				} else if (url.startsWith("jdbc:h2:")) {
					return "h2";
				}
			}
		}
		
		return "mysql"; // 默认使用 MySQL
	}
	
	/**
	 * 加载映射类
	 */
	private void loadMappingClasses() {
		String[] mappingClasses = databaseProperties.getActiveRecord().getMappingClasses();
		if (mappingClasses != null && mappingClasses.length > 0) {
			for (String className : mappingClasses) {
				try {
					log.info("加载映射类: {}", className);
					Class<?> clazz = Class.forName(className);
					Method method = clazz.getMethod("mapping", ActiveRecordPlugin.class);
					method.invoke(null, this.activeRecordPlugin);
				} catch (Exception e) {
					log.error("加载映射类失败: {}", className, e);
				}
			}
		} else {
			log.warn("未配置映射类，ActiveRecord 将无法正常工作");
		}
	}
	
	/**
	 * 配置JSON转换
	 */
	private void configureJson() {
		try {
			// 设置字段名转驼峰
			JFinalJson.setModelAndRecordFieldNameToCamelCase();
			
			// 设置跳过null值字段
			JFinalJson.setSkipNullValueField(true);
			
			// 设置字段名转换器
			JFinalJson.setModelAndRecordFieldNameConverter(fieldName -> {
				// 将下划线格式转为驼峰格式
				if (fieldName.contains("_")) {
					StringBuilder sb = new StringBuilder();
					String[] words = fieldName.split("_");
					sb.append(words[0].toLowerCase());
					for (int i = 1; i < words.length; i++) {
						if (words[i].length() > 0) {
							sb.append(Character.toUpperCase(words[i].charAt(0)));
							if (words[i].length() > 1) {
								sb.append(words[i].substring(1).toLowerCase());
							}
						}
					}
					return sb.toString();
				}
				return fieldName;
			});
			
			log.info("JFinal JSON 配置完成");
		} catch (Exception e) {
			log.warn("配置 JFinal JSON 转换器失败", e);
		}
	}
}
